| 323 | } |
| 324 | |
| 325 | pub fn do_navigate_command_string(mathml: Element, nav_command: &'static str) -> Result<String> { |
| 326 | // first check to see if nav file has been changed -- don't bother checking in loop below |
| 327 | SpeechRules::update()?; |
| 328 | NAVIGATION_RULES.with(|rules| { rules.borrow_mut().read_files() })?; |
| 329 | |
| 330 | if mathml.children().is_empty() { |
| 331 | bail!("MathML has not been set -- can't navigate"); |
| 332 | }; |
| 333 | |
| 334 | return NAVIGATION_STATE.with(|nav_state| { |
| 335 | let mut nav_state = nav_state.borrow_mut(); |
| 336 | // debug!("MathML: {}", mml_to_string(&mathml)); |
| 337 | if nav_state.position_stack.is_empty() { |
| 338 | // initialize to root node |
| 339 | nav_state.push(NavigationPosition{ |
| 340 | current_node: mathml.attribute_value("id").unwrap().to_string(), |
| 341 | current_node_offset: 0 |
| 342 | }, "None") |
| 343 | }; |
| 344 | |
| 345 | return NAVIGATION_RULES.with(|rules| { |
| 346 | let rules = rules.borrow(); |
| 347 | let new_package = Package::new(); |
| 348 | let mut rules_with_context = SpeechRulesWithContext::new(&rules, new_package.as_document(), ""); |
| 349 | |
| 350 | nav_state.mode = rules.pref_manager.as_ref().borrow().pref_to_string("NavMode"); |
| 351 | |
| 352 | nav_state.init_navigation_context(rules_with_context.get_context(), nav_command, nav_state.top()); |
| 353 | |
| 354 | // start navigation off at the right node |
| 355 | if nav_command == "MoveLastLocation" { |
| 356 | nav_state.pop(); |
| 357 | } |
| 358 | |
| 359 | // If no speech happened for some calls, we try the call the call again (e.g, no speech for invisible times). |
| 360 | // To prevent to infinite loop, we limit the number of tries |
| 361 | const LOOP_LIMIT: usize = 3; |
| 362 | let mut cumulative_speech = String::with_capacity(120); |
| 363 | for loop_count in 0..LOOP_LIMIT { |
| 364 | match apply_navigation_rules(mathml, nav_command, &rules, &mut rules_with_context, &mut nav_state, loop_count) { |
| 365 | Ok( (speech, done)) => { |
| 366 | cumulative_speech = cumulative_speech + if loop_count==0 {""} else {" "} + speech.trim(); |
| 367 | if done { |
| 368 | return Ok(cumulative_speech); |
| 369 | } |
| 370 | }, |
| 371 | Err(e) => { |
| 372 | return Err(e); |
| 373 | } |
| 374 | } |
| 375 | } |
| 376 | bail!("Internal error: Navigation exceeded limit of number of times no speech generated."); |
| 377 | }); |
| 378 | }); |
| 379 | |
| 380 | fn get_start_node<'m>(mathml: Element<'m>, nav_state: &RefMut<NavigationState>) -> Result<Element<'m>> { |
| 381 | let start_node_id = match nav_state.top() { |
| 382 | None => mathml.attribute_value("id").unwrap().to_string(), |